<?php

/*
 * 公用处理方法
 */

//截取字符串//
function getSubstr($text, $limit = 12, $endstr = "", $charset = "UTF-8") {

    if (function_exists('mb_substr')) {
        $more = mb_strlen($text, $charset) > $limit;
        $text = mb_substr($text, 0, $limit, $charset);
    } elseif (function_exists('iconv_substr')) {
        $more = iconv_strlen($text, $charset) > $limit;
        $text = iconv_substr($text, 0, $limit, $charset);
    } else {
        $limit = $charset == "UTF-8" ? $limit * 3 : $limit * 2;
        $more = strlen($text) > $limit;
        $text = substr($text, 0, $limit);

        if (ord(substr($text, -1, 1)) > 127) {
            $text = substr($text, 0, $limit - 1);
        }

        if (ord(substr($text, -1, 1)) > 127) { //uft8为三个字节，需要判断两次
            $text = substr($text, 0, $limit - 2);
        }
    }
    return $text . ( $more ? $endstr : "" );
}

/*
 * 数组进行转换
 */

function array_iconv($in_charset, $out_charset, $arr) {
    return eval('return ' . iconv($in_charset, $out_charset, var_export($arr, true) . ';'));
}

/*
 * 建立文件夹
 */

function mk_dir($dir) {
    if (!is_dir($dir)) {
        if (!mk_dir(dirname($dir))) {
            return false;
        }
        if (!mkdir($dir, 0755)) {
            return false;
        }
    }
    return true;
}

/*
 * 得到相对路径，如果没有，建立
 */

function getFilePath($prefix, $id, $file_name = "") {
    $path_id = str_pad(ceil($id / 1000), 6, 0, STR_PAD_LEFT);
    $path = $prefix . '/' . substr($path_id, 0, 3) . '/' . substr($path_id, 3, 3);
    if (!is_dir($path)) {
        mk_dir($path);
    }
    return $path . '/' . ($file_name != "" ? $file_name : $id);
}

/*
 * 得到绝对路径，没有，不会建立
 */

function getFileUrl($prefix, $id, $file_name = "") {
    $path_id = str_pad(ceil($id / 1000), 6, 0, STR_PAD_LEFT);
    $path = $prefix . '/' . substr($path_id, 0, 3) . '/' . substr($path_id, 3, 3);
    return $path . '/' . ($file_name != "" ? $file_name : $id);
}

/*
 * 写入文件内容
 */

function put_contents($file, $contents) {
    $is_up = file_put_contents($file, $contents);
    if ($is_up) {
        return true;
    } else {
        return false;
    }
}

/*
 * 得到文件内容
 */

function get_contents($file) {
    return file_get_contents($file);
}

function jsonEncode($arr, $is_ma = 1) {
    if ($is_ma == 1) {
        return json_encode($arr, JSON_UNESCAPED_UNICODE);
    } else {
        return json_encode($arr);
    }
}

function jsonDecode($arr) {
    return json_decode($arr, true);
}

function get_image_url($filename, $size = "") {
    if ($size != "" && $filename != "") {
        $ext = strrchr($filename, ".");
        $filename_arr = explode($ext, $filename);
        $filename = $filename_arr[0] . $size . $ext;
    }
    return $filename;
}

/*
 * 得到相对路径，如果没有，建立
 */

function getFilePath2($prefix, $filename) {
    $path = $prefix . '/' . date("Y") . '/' . date("m") . '/' . date("d");
    if (!is_dir($path)) {
        mk_dir($path);
    }
    return $path . '/' . md5($filename . time());
}

/*
 * 得到绝对路径，没有，不会建立
 */

function getFileUrl2($prefix, $id) {
    $path_id = str_pad($id, 12, 0, STR_PAD_LEFT);
    $path = $prefix . '/' . substr($path_id, 0, 2) . '/' . substr($path_id, 2, 2) . '/' . substr($path_id, 4, 2) . '/' . substr($path_id, 6, 2) . '/' . substr($path_id, 8, 2);
    return $path . '/' . $id;
}

/*
 * 得到IP地址
 */

function get_ip() {
    $realip = "";
    if (isset($_SERVER)) {
        if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
            $realip = $_SERVER["HTTP_X_FORWARDED_FOR"];
        } else if (isset($_SERVER["HTTP_CLIENT_IP"])) {
            $realip = $_SERVER["HTTP_CLIENT_IP"];
        } else {
            $realip = $_SERVER["REMOTE_ADDR"];
        }
    } else {
        if (getenv('HTTP_X_FORWARDED_FOR')) {
            $realip = getenv('HTTP_X_FORWARDED_FOR');
        } else if (getenv('HTTP_CLIENT_IP')) {
            $realip = getenv('HTTP_CLIENT_IP');
        } else {
            $realip = getenv('REMOTE_ADDR');
        }
    }
    return $realip;
}

function int_val($num) {
    return intval(strval($num));
}

function isMobile($mobile) {
    $result = true;
    if (!preg_match("/^1[3456789]\d{9}$/", $mobile)) {
        $result = false;
    }
    return $result;
}

//验证身份证
function isIdCard($id) {
    $id = strtoupper($id);
    $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/";
    $arr_split = array();
    if (!preg_match($regx, $id)) {
        return FALSE;
    }
    if (15 == strlen($id)) { //检查15位
        $regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/";

        @preg_match($regx, $id, $arr_split);
        //检查生日日期是否正确
        $dtm_birth = "19" . $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
        if (!strtotime($dtm_birth)) {
            return FALSE;
        } else {
            return TRUE;
        }
    } else {      //检查18位
        $regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
        @preg_match($regx, $id, $arr_split);
        $dtm_birth = $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
        if (!strtotime($dtm_birth)) { //检查生日日期是否正确
            return FALSE;
        } else {
            //检验18位身份证的校验码是否正确。
            //校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
            $arr_int = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
            $arr_ch = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
            $sign = 0;
            for ($i = 0; $i < 17; $i++) {
                $b = (int) $id[$i];
                $w = $arr_int[$i];
                $sign += $b * $w;
            }
            $n = $sign % 11;
            $val_num = $arr_ch[$n];
            if ($val_num != substr($id, 17, 1)) {
                return FALSE;
            } //phpfensi.com
            else {
                return TRUE;
            }
        }
    }
}

/**
 * 中国正常GCJ02坐标---->百度地图BD09坐标
 * 腾讯地图用的也是GCJ02坐标
 * @param double $lat 纬度
 * @param double $lng 经度
 */
function convert_GCJ02_To_BD09($lng = 0, $lat = 0) {
    $x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    $x = $lng;
    $y = $lat;
    $z = sqrt($x * $x + $y * $y) + 0.00002 * sin($y * $x_pi);
    $theta = atan2($y, $x) + 0.000003 * cos($x * $x_pi);
    $lng = $z * cos($theta) + 0.0065;
    $lat = $z * sin($theta) + 0.006;
    return array('lng' => $lng, 'lat' => $lat);
}

/**
 * 百度地图BD09坐标---->中国正常GCJ02坐标
 * 腾讯地图用的也是GCJ02坐标
 * @param double $lat 纬度
 * @param double $lng 经度
 * @return array();
 */
function convert_BD09_To_GCJ02($lng = 0, $lat = 0) {
    $x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    $x = $lng - 0.0065;
    $y = $lat - 0.006;
    $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
    $theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
    $lng = $z * cos($theta);
    $lat = $z * sin($theta);
    return array('lng' => $lng, 'lat' => $lat);
}

//生成随机的 guid
function createGuid() {
    $charid = strtoupper(md5(uniqid(mt_rand(), true)));
    $hyphen = chr(45); // "-"
    $uuid = substr($charid, 6, 2) . substr($charid, 4, 2) .
            substr($charid, 2, 2) . substr($charid, 0, 2) . $hyphen
            . substr($charid, 10, 2) . substr($charid, 8, 2) . $hyphen
            . substr($charid, 14, 2) . substr($charid, 12, 2) . $hyphen
            . substr($charid, 16, 4) . $hyphen . substr($charid, 20, 12);
    return $uuid;
}

/*
 * 得到唯一ID
 */

function uniqueId($suffix = "") {
    $mTime = microtime();
    $mTimes = explode(' ', $mTime);
    return time() . $suffix . round($mTimes[0] * 1000000, 0);
}

/*
 * 检查文件源文件是否为图片
 */

function check_img_by_source($source) {
    switch (bin2hex(substr($source, 0, 2))) {
        case 'ffd8' :
            return 'ffd9' === bin2hex(substr($source, -2));
        case '8950' :
            return '6082' === bin2hex(substr($source, -2));
        case '4749' :
            return '003b' === bin2hex(substr($source, -2));
        default :
            return false;
    }
}

/*
 * 判断是否为图片格式
 */

function check_is_image($data) {
//    $im = imagecreatefromstring($data);
//    if ($im !== false) {
//        return true;
//    } else {
//        return false;
//    }
    $source = $data;
    //echo bin2hex(substr($source, 0, 2));
    //echo bin2hex(substr($source, -2));
    switch (bin2hex(substr($source, 0, 2))) {
//        case 'ffd8' : return 'ffd9' === bin2hex(substr($source, -2));
//        case '8950' : return '6082' === bin2hex(substr($source, -2));
//        case '4749' : return '003b' === bin2hex(substr($source, -2));
        case 'ffd8' : return true;
        case '8950' : return true;
        case '4749' : return true;
        default : return false;
    }
}

function setTime($type = 1) {
    $time = time();
    if ($type == 1) {
        $time = time();
    } else if ($type == 2) {
        $time = date("Y-m-d H:i:s");
    } else if ($type == 3) {
        $time = date("Y-m-d");
    }
    return $time;
}

function isJson($string) {
    $result = json_decode($string, true);
    return $result !== null && $result !== $string;
}

/*
 * 两数组比较，取得具有相同值的第一个数组值
 */

function getSameArrayValue($array1 = [], $array2 = []) {

    $str = "";
    $arr = [];
    $commonValues = array_intersect($array1, $array2);
    if (!empty($commonValues)) {
        foreach ($commonValues as $k => $v) {
            $arr[] = $v;
        }
        $str = $arr[0];
    }
    return $str;
}

// 定义前N个质数对应的位位置
$primeBitmask = [
    2 => 1 << 0, // 第0位 (1)
    3 => 1 << 1, // 第1位 (2)
    5 => 1 << 2, // 第2位 (4)
    7 => 1 << 3, // 第3位 (8)
    11 => 1 << 4, // 第4位 (16)
    13 => 1 << 5, // 第4位 (32)
    17 => 1 << 6, // 第4位 (64)
        // 可以继续添加更多质数
];
/*
 * 质数转换为位
 */

function primesToBitmask(array $primes, array $primeBitmask): int {
    $bitmask = 0;
    foreach ($primes as $prime) {
        if (isset($primeBitmask[$prime])) {
            $bitmask |= $primeBitmask[$prime];
        }
    }
    return $bitmask;
}

/*
 * 位转换为质数
 */

function bitmaskToPrimes(int $bitmask, array $primeBitmask): array {
    $primes = [];
    foreach ($primeBitmask as $prime => $mask) {
        if ($bitmask & $mask) {
            $primes[] = $prime;
        }
    }
    return $primes;
}

/**
 * 反向拆解位运算相加后的数值
 * @param int $num 需要拆解的数值（需为正整数）
 * @return array 包含所有独立位值的数组
 */
function reverseBitComponents($num) {
    $components = [];
    $currentBit = 1; // 从最低位 1（2^0）开始
    while ($currentBit <= $num) {
        // 检查当前位是否存在
        if ($num & $currentBit) {
            $components[] = $currentBit;
        }
        $currentBit <<= 1; // 左移一位（即乘以2）
    }
    return $components;
}

/**
 * 校验集装箱箱号是否符合ISO 6346标准
 * @param string $containerNo 集装箱箱号（11位）
 * @return bool 校验结果
 */
function validateContainerNumber($containerNo) {
    // 去除空格和特殊字符，只保留字母数字
    $containerNo = preg_replace('/[^A-Za-z0-9]/', '', strtoupper($containerNo));

    // 检查长度
    if (strlen($containerNo) != 11) {
        return false;
    }

    // 提取前10位和校验位
    $first10 = substr($containerNo, 0, 10);
    $checkDigit = substr($containerNo, 10, 1);

    // 计算校验码
    $calculatedCheckDigit = calculateCheckDigit($first10);

    // 比较校验码
    return $checkDigit == $calculatedCheckDigit;
}

/**
 * 计算集装箱箱号的校验码
 * @param string $first10 前10位字符
 * @return string 校验码
 */
function calculateCheckDigit($first10) {
    // 字母转换映射 (A=10, B=12, C=13, D=14, E=15, F=16, G=17, H=18, I=19, 
    // J=20, K=21, L=23, M=24, N=25, O=26, P=27, Q=28, R=29, S=30, T=31, 
    // U=32, V=34, W=35, X=36, Y=37, Z=38)
    // 注意：跳过了11,22,33
    $letterMap = array(
        'A' => 10, 'B' => 12, 'C' => 13, 'D' => 14, 'E' => 15, 'F' => 16, 'G' => 17, 'H' => 18, 'I' => 19,
        'J' => 20, 'K' => 21, 'L' => 23, 'M' => 24, 'N' => 25, 'O' => 26, 'P' => 27, 'Q' => 28, 'R' => 29,
        'S' => 30, 'T' => 31, 'U' => 32, 'V' => 34, 'W' => 35, 'X' => 36, 'Y' => 37, 'Z' => 38
    );

    // 加权因子 - 使用2的幂次方 [2^0, 2^1, 2^2, ..., 2^9] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
    $weights = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];

    $sum = 0;

    // 遍历前10位字符
    for ($i = 0; $i < 10; $i++) {
        $char = $first10[$i];

        // 如果是字母，转换为对应的数字
        if (ctype_alpha($char)) {
            $char = strtoupper($char);
            if (!isset($letterMap[$char])) {
                return false; // 无效字母
            }
            $value = $letterMap[$char];
        }
        // 如果是数字，直接使用
        elseif (ctype_digit($char)) {
            $value = (int) $char;
        }
        // 无效字符
        else {
            return false;
        }

        // 加权求和
        $sum += $value * $weights[$i];
    }

    // 计算校验码
    $remainder = $sum % 11;

    // 根据ISO 6346标准，余数为10时校验码为0
    if ($remainder == 10) {
        return '0';
    } else {
        return (string) $remainder;
    }
}

/**
 * 详细验证并返回验证信息
 * @param string $containerNo 集装箱箱号
 * @return array 验证结果详情
 */
function validateContainerNumberDetailed($containerNo) {
    $result = [
        'is_valid' => false,
        'original_number' => $containerNo,
        'normalized_number' => '',
        'calculated_check_digit' => '',
        'actual_check_digit' => '',
        'error_message' => '',
        'calculation_details' => []
    ];

    // 标准化箱号
    $normalizedNo = preg_replace('/[^A-Za-z0-9]/', '', strtoupper($containerNo));
    $result['normalized_number'] = $normalizedNo;

    // 检查长度
    if (strlen($normalizedNo) != 11) {
        $result['error_message'] = "箱号长度应为11位，当前为" . strlen($normalizedNo) . "位";
        return $result;
    }

    // 提取前10位和校验位
    $first10 = substr($normalizedNo, 0, 10);
    $actualCheckDigit = substr($normalizedNo, 10, 1);
    $result['actual_check_digit'] = $actualCheckDigit;

    // 计算校验码
    $calculatedCheckDigit = calculateCheckDigit($first10);
    $result['calculated_check_digit'] = $calculatedCheckDigit;

    if ($calculatedCheckDigit === false) {
        $result['error_message'] = "箱号包含无效字符";
        return $result;
    }

    // 比较校验码
    $result['is_valid'] = ($actualCheckDigit == $calculatedCheckDigit);

    if (!$result['is_valid']) {
        $result['error_message'] = "校验码不匹配，实际为{$actualCheckDigit}，计算应为{$calculatedCheckDigit}";
    }

    // 添加详细计算过程
    $result['calculation_details'] = getCalculationDetails($first10);

    return $result;
}

/**
 * 获取详细的计算过程
 * @param string $first10 前10位字符
 * @return array 计算详情
 */
function getCalculationDetails($first10) {
    $letterMap = array(
        'A' => 10, 'B' => 12, 'C' => 13, 'D' => 14, 'E' => 15, 'F' => 16, 'G' => 17, 'H' => 18, 'I' => 19,
        'J' => 20, 'K' => 21, 'L' => 23, 'M' => 24, 'N' => 25, 'O' => 26, 'P' => 27, 'Q' => 28, 'R' => 29,
        'S' => 30, 'T' => 31, 'U' => 32, 'V' => 34, 'W' => 35, 'X' => 36, 'Y' => 37, 'Z' => 38
    );

    $weights = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];

    $details = [];
    $sum = 0;

    for ($i = 0; $i < 10; $i++) {
        $char = $first10[$i];

        if (ctype_alpha($char)) {
            $char = strtoupper($char);
            $value = $letterMap[$char];
        } else {
            $value = (int) $char;
        }

        $weight = $weights[$i];
        $product = $value * $weight;
        $sum += $product;

        $details[] = [
            'position' => $i + 1,
            'character' => $char,
            'value' => $value,
            'weight' => $weight,
            'product' => $product
        ];
    }

    $remainder = $sum % 11;
    $checkDigit = $remainder == 10 ? '0' : (string) $remainder;

    return [
        'steps' => $details,
        'sum' => $sum,
        'remainder' => $remainder,
        'check_digit' => $checkDigit
    ];
}
